/*
 * 创建日期 2007-10-10
 *
 * 成都天和软件公司
 * 电话：028-85425861
 * 传真：028-85425861-8008
 * 邮编：610041 
 * 版权所有
 */
package  org.fantasy.common.util;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import net.sf.json.JSONObject;

/**
 * 属性拷贝
 * @author: 王文成
 * @version: 1.0
 * @since 2009-3-17
 */
public class CopyUtil {
	private static final String SET = "set";
	
	private static final String GET = "get";

	/**
	 * 复制属性
	 * 
	 * @param dest
	 * @param rs
	 * @throws Exception
	 */
	public static void copyProperties(Object dest, Map orig) throws Exception {
		new PropertyCopy(dest, orig).copyProperties();
	}
	
	/**
	 * 复制属性
	 * @param dest
	 * @param orig
	 * @throws Exception
	 */
	@SuppressWarnings("all")
	public static void copyProperties(Object dest, Object orig) throws Exception {
		Class<?> destClass = dest.getClass();
		Method[] methods = orig.getClass().getDeclaredMethods();
		for (Method method : methods) {
			String getMethodName = method.getName();
			boolean noParams = method.getParameterTypes().length == 0;
			boolean isPublic = Modifier.isPublic(method.getModifiers());
			boolean isGetMethod = getMethodName.startsWith(GET);
			if( isPublic && isGetMethod && noParams){
				Object value = method.invoke(orig,null);
				String setMethodName = getMethodName.replaceFirst(GET,SET);
				Method destMethod = destClass.getMethod(setMethodName, null);
				destMethod.invoke(dest, value);
			}
		}
	}

	/**
	 * 复制属性
	 * 
	 * @param dest
	 * @param rs
	 * @throws Exception
	 */
	public static void copyProperties(Object dest, ResultSet rs) throws Exception {
		ResultSetMetaData metadata = rs.getMetaData();
		int n = metadata.getColumnCount();
		HashMap<Object, Object> origMap = new HashMap<Object, Object>();
		for (int i = 1; i <= n; i++) {

			String colName = metadata.getColumnName(i);
			Object value = rs.getObject(colName);
			String fieldName = StringUtil.getFieldString(colName);
			origMap.put(fieldName, value);
		}
		new PropertyCopy(dest, origMap).copyProperties();
	}

	/**
	 * 从JSONObject转换成javabean
	 * 
	 * @param dest
	 * @param jsonObject
	 * @throws Exception
	 */
	public static void copyProperties(Object dest, JSONObject jsonObject) throws Exception {
		Map<String, Object> origMap = new HashMap<String, Object>();
		for (Iterator<String> keys = jsonObject.keys(); keys.hasNext();){
			String key = (String) keys.next();
			origMap.put(key, jsonObject.get(key));
		}
		new PropertyCopy(dest, origMap).copyProperties();
	}

	/**
	 * 属性拷贝
	 */
	private static class PropertyCopy {
		private Map methodMap = new HashMap();

		private Object dest;

		private Map source;

		public PropertyCopy(Object dest, Map source) {
			this.dest = dest;
			this.source = source;
			this.init();
		}

		/**
		 * 初始化参数
		 * 
		 * @param dest
		 * @param source
		 */
		private void init() {
			Class cls = dest.getClass();
			Method[] methods = cls.getMethods();
			for (int i = 0; i < methods.length; i++) {
				Method mtd = methods[i];
				// 只保存公共方法&&set开头
				if (Modifier.isPublic(mtd.getModifiers()) && mtd.getName().startsWith(SET)) {
					this.methodMap.put(mtd.getName(), mtd);
				}
			}
		}

		/**
		 * 参数复制
		 * 
		 * @param dest
		 * @param source
		 * @throws Exception
		 */
		public void copyProperties() throws Exception {
			for (Iterator iter = source.entrySet().iterator(); iter.hasNext();) {
				Map.Entry entry = (Map.Entry) iter.next();
				String key = (String) entry.getKey();
				Object value = entry.getValue();
				// 针对 org.fantasy.common.db.Row 的类型，因为取出来是Cell接口，所以需要转换
				if (isCell(value)) {
					value = value.toString();
				}
				try {
					this.copy(key, value);
				} catch (Exception e) {
					System.out.println(e);
				}
			}
		}

		/**
		 * 参数复制
		 * 
		 * @param dest
		 * @param name
		 * @param value
		 * @throws Exception
		 */
		private void copy(String name, Object value) throws Exception {
			// 如果方法不可写(方法Public不存在或者参数不匹配不做操作)
			if (!this.isWirteable(name))
				return;
			// 取得参数类型
			Class cls = this.getArgumentClass(name);
			Method method = this.getMethod(name);
			// 形参和实参类型相同 直接调用方法
			if (cls.equals(value.getClass())) {
				method.invoke(dest, new Object[] { value });
			} else {
				// 做类型转换
				this.conversion(cls, method, value);
			}
		}

		/**
		 * 转换类型
		 * 
		 * @throws Exception
		 */
		private void conversion(Class cls, Method method, Object value) throws Exception {
			// 如果数据无效置null
			if (!StringUtil.isValid(value)) {
				method.invoke(dest, new Object[] { null });
				return;
			}
			// 如果值是String类型 进行对应的类型转换(目前只满足字符串转化为数字)
			if (value instanceof java.lang.String) {
				if (cls.equals(Long.class) || cls.equals(Integer.class) || cls.equals(Short.class) || cls.equals(Byte.class) || cls.equals(Double.class)
						|| cls.equals(Float.class)) {
					Method valueOfMethod = cls.getMethod("valueOf", new Class[] { String.class });
					Object invokeValue = valueOfMethod.invoke(null, new Object[] { value });
					method.invoke(dest, new Object[] { invokeValue });
				}
			}

		}

		/**
		 * 判断方法是否有效 存在该方法&&参数长度只能为1
		 * 
		 * @param name
		 * @return
		 */
		private boolean isWirteable(String name) {
			Method method = this.getMethod(name);
			if (method == null)
				return false;
			if (method.getParameterTypes().length != 1)
				return false;
			return true;
		}

		/**
		 * 取得方法参数类型
		 * 
		 * @param name
		 * @return
		 * @throws Exception
		 */
		private Class getArgumentClass(String name) throws Exception {
			Method method = this.getMethod(name);
			Class[] cls = method.getParameterTypes();
			return cls[0];
		}

		/**
		 * 取得对应方法
		 * 
		 * @param name
		 * @return
		 */
		private Method getMethod(String name) {
			String methodName = SET + this.upperCaseFirstChar(name);
			return (Method) methodMap.get(methodName);
		}

		/**
		 * 第一个字符大写
		 * 
		 * @param value
		 * @return
		 */
		private String upperCaseFirstChar(String value) {
			return String.valueOf(value.charAt(0)).toUpperCase() + value.substring(1);
		}

		/**
		 * 判断Map中的值的类型是否Cell接口
		 * 
		 * @param value
		 * @return
		 */
		private boolean isCell(Object value) {
			return  org.fantasy.common.db.bean.Cell.class.isAssignableFrom(value.getClass());
		}
	}
}
